package com.chat.admin.Listener;


import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import com.chat.admin.domain.pojo.UserExcelEnter;
import com.chat.admin.service.UserManagementService;
import com.chat.admin.service.impl.UserManagementServiceImpl;
import com.chat.admin.utils.SpringContextUtil;
import com.chat.common.common.exception.BaseException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 自定义监听器读取用户Excel数据
 */
@Slf4j
public class ReadUserExcelDataListener implements ReadListener<UserExcelEnter> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 失败的数据列表 最大1000
     */
    private static final int BATCH_FAIL_COUNT = 10000;
    /**
     * 缓存的数据
     */
    private List<UserExcelEnter> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * （JSR 380）
     */
    private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    /**
     * 失败的数据
     */
    private List<Map<String, Object>> failedDataList = new ArrayList<>();

    // 预期的表头
    private final List<String> expectedHead = Arrays.asList("账号", "用户名", "密码", "邮箱", "电话", "性别", "年龄");

    private UserManagementService userManagementService;

    public ReadUserExcelDataListener(){
        this.userManagementService = SpringContextUtil.getBean(UserManagementService.class);;
    }

    public ReadUserExcelDataListener(UserManagementServiceImpl userManagementService){
        this.userManagementService = userManagementService;
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
        // 将headMap转换为Map<Integer, String>
        Map<Integer, String> stringHeadMap = ConverterUtils.convertToStringMap(headMap, context);
        // 检查表头是否符合预期
        if (!expectedHead.equals(new ArrayList<>(stringHeadMap.values()))) {
            throw new BaseException("表头不符合预期");
        }
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param userExcelEnter
     * @param analysisContext
     */
    @Override
    public void invoke(UserExcelEnter userExcelEnter, AnalysisContext analysisContext) {
        log.info("解析到一条数据:{}", JSON.toJSONString(userExcelEnter));
        // 使用Validator对象来校验数据
        Set<ConstraintViolation<UserExcelEnter>> violations = validator.validate(userExcelEnter);
        if (!violations.isEmpty()) {
            // 如果有校验失败的情况，将数据和错误信息添加到failedDataList
            String errorMessages = violations.stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.joining(", "));
            Map<String, Object> failedData = new HashMap<>();
            failedData.put("data", userExcelEnter);
            failedData.put("error", errorMessages);
            failedDataList.add(failedData);
            // 如果失败的数据超过一定数量，抛出异常
            if (failedDataList.size() >= BATCH_FAIL_COUNT) {
                throw new BaseException("数据校验失败次数过多");
            }
        } else {
            cachedDataList.add(userExcelEnter);
        }
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 结束执行的操作
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成！");
    }

    private void saveData() {
        log.info("{}条数据，开始存储数据库！", cachedDataList.size());
        userManagementService.convertToUsers(cachedDataList);
        log.info("存储数据库成功！");
    }
}
